package ph.com.gs3.formalistics.model.rdao;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import ph.com.gs3.formalistics.global.utilities.Serializer;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.model.HttpCommunicator;
import ph.com.gs3.formalistics.model.RemoteDataAccessObject;

public class FilesRDAO extends RemoteDataAccessObject {

	public static final String TAG = FilesRDAO.class.getSimpleName();

	public FilesRDAO(String server) {
		super(new HttpCommunicator(), server);
	}

	public void uploadFilesTest(List<String> fileURIList, String uploadFolderDestination ) throws FileUploadFailedException {
		
		HttpURLConnection conn = null;
		DataOutputStream dos = null;
		String lineEnd = "\r\n";
		String twoHyphens = "--";
		String boundary = "*****";
		int bytesRead, bytesAvailable, bufferSize;
		byte[] buffer;
		int maxBufferSize = 1 * 1024 * 1024;

		List<File> sourceFileList = new ArrayList<>();
		List<String> sourceFileAbsolutePathList = new ArrayList<>();

		URL uploadURL;

		// Load each URI to a File object
		for (String fileURI : fileURIList) {
			File sourceFile = new File(fileURI);

			if (!sourceFile.isFile()) {
				throw new FileUploadFailedException("Cannot find file: " + fileURI);
			}

			sourceFileList.add(sourceFile);
			sourceFileAbsolutePathList.add(sourceFile.getAbsolutePath());
		}

		// Do initializations that can throw exceptions
		try {
			// MalformedURLException
			uploadURL = new URL(server + "/file/upload");

			// IOException
			conn = (HttpURLConnection) uploadURL.openConnection();

			// ProtocolException
			conn.setRequestMethod("POST");
		} catch (MalformedURLException | ProtocolException e) {
			// Code Level Exceptions
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new FileUploadFailedException("Could open a connection to " + server, e);
		}

		// Do the rest of the initializations that can throw exceptions
		conn.setDoInput(true); // Allow Inputs
		conn.setDoOutput(true); // Allow Outputs
		conn.setUseCaches(false); // Don't use a Cached Copy

		conn.setRequestProperty("Connection", "Keep-Alive");
		conn.setRequestProperty("ENCTYPE", "multipart/form-data");
		conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

		for (String fileURI : fileURIList) {
			conn.addRequestProperty("file", fileURI);
		}
		

		try {
			// Write each files into the output stream
			dos = new DataOutputStream(conn.getOutputStream());

			FLLogger.d(TAG, "Content-Disposition: form-data; name=\"file\";filename=\""
			        + Serializer.serializeList(sourceFileAbsolutePathList) + "\"");

			dos.writeBytes(twoHyphens + boundary + lineEnd);

			File sourceFile = sourceFileList.get(0);
			
			dos.writeBytes("Content-Disposition: form-data; name=\"file[]\";");
			dos.writeBytes("filename=\"" + sourceFile.getAbsolutePath() + "\"");
			dos.writeBytes(lineEnd);

			FileInputStream sourceFileInputStream = null;

			try {
				sourceFileInputStream = new FileInputStream(sourceFile);
				// Create a buffer of maximum size
				bytesAvailable = sourceFileInputStream.available();

				bufferSize = Math.min(bytesAvailable, maxBufferSize);
				buffer = new byte[bufferSize];

				// Read file and write it into form
				bytesRead = sourceFileInputStream.read(buffer, 0, bufferSize);

				while (bytesRead > 0) {
					dos.write(buffer, 0, bufferSize);
					bytesAvailable = sourceFileInputStream.available();
					bufferSize = Math.min(bytesAvailable, maxBufferSize);
					bytesRead = sourceFileInputStream.read(buffer, 0, bufferSize);
				}
			} finally {
				if (sourceFileInputStream != null) {
					sourceFileInputStream.close();
				}
			}

			// Send multipart form data necessary after file data...
			dos.writeBytes(lineEnd);
			dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

			// // Write other parameters
			// Map<String, String> parameters = new HashMap<>();
			// parameters.put("target_directory", uploadFolderDestination);
			//
			// dos.writeBytes(lineEnd);
			// dos.writeBytes(HttpCommunicator.createQueryString(parameters));

			// Responses from the server (code and message)
			int serverResponseCode = conn.getResponseCode();
			String serverResponseMessage = conn.getResponseMessage();

			FLLogger.d(TAG, "HTTP Response is : " + serverResponseMessage + ": "
			        + serverResponseCode);

			FLLogger.d(TAG, HttpCommunicator.parseInputStream(conn.getInputStream()));

			dos.flush();
			dos.close();

		} catch (IOException e) {
			throw new FileUploadFailedException(e);
		} finally {
			conn.disconnect();
		}
		
	}
	
	public void uploadFiles(List<String> fileURIList, String uploadFolderDestination)
	        throws FileUploadFailedException {

		HttpURLConnection conn = null;
		DataOutputStream dos = null;
		String lineEnd = "\r\n";
		String twoHyphens = "--";
		String boundary = "*****";
		int bytesRead, bytesAvailable, bufferSize;
		byte[] buffer;
		int maxBufferSize = 1 * 1024 * 1024;

		List<File> sourceFileList = new ArrayList<>();
		List<String> sourceFileAbsolutePathList = new ArrayList<>();

		URL uploadURL;

		// Load each URI to a File object
		for (String fileURI : fileURIList) {
			File sourceFile = new File(fileURI);

			if (!sourceFile.isFile()) {
				throw new FileUploadFailedException("Cannot find file: " + fileURI);
			}

			sourceFileList.add(sourceFile);
			sourceFileAbsolutePathList.add(sourceFile.getAbsolutePath());
		}

		// Do initializations that can throw exceptions
		try {
			// MalformedURLException
			uploadURL = new URL(server + "/file/upload");

			// IOException
			conn = (HttpURLConnection) uploadURL.openConnection();

			// ProtocolException
			conn.setRequestMethod("POST");
		} catch (MalformedURLException | ProtocolException e) {
			// Code Level Exceptions
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new FileUploadFailedException("Could open a connection to " + server, e);
		}

		// Do the rest of the initializations that can throw exceptions
		conn.setDoInput(true); // Allow Inputs
		conn.setDoOutput(true); // Allow Outputs
		conn.setUseCaches(false); // Don't use a Cached Copy

		conn.setRequestProperty("Connection", "Keep-Alive");
		conn.setRequestProperty("ENCTYPE", "multipart/form-data");
		conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

		for (String fileURI : fileURIList) {
			conn.addRequestProperty("file", fileURI);
		}

		try {
			// Write each files into the output stream
			dos = new DataOutputStream(conn.getOutputStream());

			FLLogger.d(TAG, "Content-Disposition: form-data; name=\"file\";filename=\""
			        + Serializer.serializeList(sourceFileAbsolutePathList) + "\"");

			dos.writeBytes(twoHyphens + boundary + lineEnd);

			for (File sourceFile : sourceFileList) {

				dos.writeBytes("Content-Disposition: form-data; name=\"file[]\";");
				dos.writeBytes("filename=\"" + sourceFile.getAbsolutePath() + "\"");
				dos.writeBytes(lineEnd);

				FileInputStream sourceFileInputStream = null;

				try {
					sourceFileInputStream = new FileInputStream(sourceFile);
					// Create a buffer of maximum size
					bytesAvailable = sourceFileInputStream.available();

					bufferSize = Math.min(bytesAvailable, maxBufferSize);
					buffer = new byte[bufferSize];

					// Read file and write it into form
					bytesRead = sourceFileInputStream.read(buffer, 0, bufferSize);

					while (bytesRead > 0) {
						dos.write(buffer, 0, bufferSize);
						bytesAvailable = sourceFileInputStream.available();
						bufferSize = Math.min(bytesAvailable, maxBufferSize);
						bytesRead = sourceFileInputStream.read(buffer, 0, bufferSize);
					}
				} finally {
					if (sourceFileInputStream != null) {
						sourceFileInputStream.close();
					}
				}

			}

			// Send multipart form data necessary after file data...
			dos.writeBytes(lineEnd);
			dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

			// // Write other parameters
			// Map<String, String> parameters = new HashMap<>();
			// parameters.put("target_directory", uploadFolderDestination);
			//
			// dos.writeBytes(lineEnd);
			// dos.writeBytes(HttpCommunicator.createQueryString(parameters));

			// Responses from the server (code and message)
			int serverResponseCode = conn.getResponseCode();
			String serverResponseMessage = conn.getResponseMessage();

			FLLogger.d(TAG, "HTTP Response is : " + serverResponseMessage + ": "
			        + serverResponseCode);

			FLLogger.d(TAG, HttpCommunicator.parseInputStream(conn.getInputStream()));

			dos.flush();
			dos.close();

		} catch (IOException e) {
			throw new FileUploadFailedException(e);
		} finally {
			conn.disconnect();
		}

	}

	public static class FileUploadFailedException extends Exception {

		private static final long serialVersionUID = -877051381624851249L;

		public FileUploadFailedException(String message) {
			super(message);
		}

		public FileUploadFailedException(Throwable throwable) {
			super(throwable);
		}

		public FileUploadFailedException(String message, Throwable throwable) {
			super(message, throwable);
		}

	}

}
